<?php
/**
 * This file contains the code to parse faces-config.xml
 * 
 * @license http://www.gnu.org/licenses/lgpl.html LGPL
 * @author Harlan Iverson <h dot iverson at gmail dot com>
 * @copyright Copyright (c) 2006, Harlan Iverson.
 * @version $Id: FacesConfig.php 20 2006-08-01 05:54:40Z Harlan $
 */
 

/**
 * A navigation rule entry
 */
class PhpFaces_NavigationRuleConfigEntry {
	public $fromViewId;
	public $toViewId;
	public $fromAction;	
	public $fromOutcome;
	
	public function __construct( $fromViewId = null, $fromAction = null, $fromOutcome = null, $toViewId = null ) {
		$this->fromViewId = $fromViewId;
		$this->toViewId = $toViewId;
		$this->fromAction = $fromAction;	
		$this->fromOutcome = $fromOutcome;
	}
}

/**
 * A managed bean entry
 */
class PhpFaces_ManagedBeanConfigEntry {
	public $name;
	public $class;
	public $scope;
}


/**
 * The faces-config.xml parser class. It is given a file
 * path and makes accessor properties available.
 * 
 * It takes a regular format faces-config.xml as is used by JSF,
 * but also parses an additional element: phpfaces:require. Since
 * PHP doesn't have a "class path," this element simply lets you 
 * require files before the various beans or view/navigation handlers
 * are instantiated.
 */
class PhpFaces_FacesConfig {
	public $path;
	public $requireFiles;
	public $managedBeans;
	public $navigationRules;
	
	public $navigationHandlers;
	public $viewHandlers;
	
	/**
	 * Constructor
	 * @param $path string the path to the config file
	 */
	public function __construct( $path ) {
		$this->path = $path;
		$this->requireFiles = array();
		$this->managedBeans = array();
		$this->navigationRules = array();
		$this->navigationHandlers = array();
		$this->viewHandlers = array();
	}
	
	/**
	 * Parse the file given in the constructor
	 */
	public function parse() {
		$configXml = new XmlReader();
		$configXml->open( $this->path );
		$configXml->setParserProperty(2,true); // This seems a little unclear to me - but it worked :)
		
		$isInConfig = false;
		
		$managedBean = null;
		$textBuffer = "";
				
		while( $configXml->read() ) {
			
			if( $configXml->nodeType == XMLReader::TEXT
				// || $configXml->nodeType == XMLReader::SIGNIFICANT_WHITESPACE
				|| $configXml->nodeType == XMLReader::CDATA ) {
					
				$textBuffer .= $configXml->value;
					
				continue;		
			}
			
			switch( $configXml->namespaceURI ) {
				case "":
					switch( $configXml->localName ) {
						case "application":
							if( $configXml->nodeType == XMLReader::ELEMENT ) {
								$isInApplication = true;
							} else if( $configXml->nodeType == XMLReader::END_ELEMENT ) {
								$isInApplication = false;
							}
							break;
						case "navigation-handler":
							if( $configXml->nodeType == XMLReader::ELEMENT ) {
								$navigationHandler = "";
								$textBuffer =& $navigationHandler;
							} else if ( $configXml->nodeType == XMLReader::END_ELEMENT ) {
								$textBuffer = trim( $textBuffer );
								$this->navigationHandlers[] = $textBuffer;
								unset( $textBuffer );
							}
							break;
						case "view-handler":
							if( $configXml->nodeType == XMLReader::ELEMENT ) {
								$viewHandler  = "";
								$textBuffer =& $viewHandler;
							} else if ( $configXml->nodeType == XMLReader::END_ELEMENT ) {
								$textBuffer = trim( $textBuffer );
								$this->viewHandlers[] = $textBuffer;
								unset( $textBuffer );
							}
							break;
						case "managed-bean":
							if( $configXml->nodeType == XMLReader::ELEMENT ) {
								$managedBean = new PhpFaces_ManagedBeanConfigEntry();
							} else if( $configXml->nodeType == XMLReader::END_ELEMENT ) {
								$this->managedBeans[] = $managedBean;
								$managedBean = null;
							}
							break;
							
						case "managed-bean-name":
							if( $configXml->nodeType == XMLReader::ELEMENT ) {
								$textBuffer =& $managedBean->name;
							} else if ( $configXml->nodeType == XMLReader::END_ELEMENT ) {
								$textBuffer = trim( $textBuffer );
								unset( $textBuffer );
							}
							break;
						case "managed-bean-class":
							if( $configXml->nodeType == XMLReader::ELEMENT ) {
								$textBuffer =& $managedBean->class;
							} else if ( $configXml->nodeType == XMLReader::END_ELEMENT ) {
								$textBuffer = trim( $textBuffer );
								unset( $textBuffer );
							}
							break;
						case "managed-bean-scope":
							if( $configXml->nodeType == XMLReader::ELEMENT ) {
								$textBuffer =& $managedBean->scope;
							} else if ( $configXml->nodeType == XMLReader::END_ELEMENT ) {
								$textBuffer = trim( $textBuffer );
								unset( $textBuffer );
							}
							break;
						case "navigation-rule":
							if( $configXml->nodeType == XMLReader::ELEMENT ) {
								$navRule = new PhpFaces_NavigationRuleConfigEntry();
							} else if ( $configXml->nodeType == XMLReader::END_ELEMENT ) {
								$this->navigationRules[] = $navRule;
								unset( $navRule );
							}
							break;
						// handle all cases the same, but put them in different places
						case "from-view-id":
						case "to-view-id":
						case "from-action":
						case "from-outcome":
							if( $configXml->nodeType == XMLReader::ELEMENT ) {
								switch( $configXml->localName ) {
									case "from-view-id":
										$textBuffer =& $navRule->fromViewId;
										break;
									case "to-view-id":
										$textBuffer =& $navRule->toViewId;
										break;
									case "from-action":
										$textBuffer =& $navRule->fromAction;
										break;
									case "from-outcome":
										$textBuffer =& $navRule->fromOutcome;
										break;
								}
							} else if ( $configXml->nodeType == XMLReader::END_ELEMENT ) {
								$textBuffer = trim( $textBuffer );
								unset( $textBuffer );
							}
							break;
						
					}
					break;
				case "urn:php-faces":
					switch( $configXml->localName ) {
						case "require":
							if( $isInApplication && ($configXml->nodeType == XMLReader::ELEMENT || $configXml->isEmptyElement) ) {
								$this->requireFiles[] = $configXml->getAttribute( "file" );
							} 
							break;
					}
			}
			
		}

	}
	
	/**
	 * Get an array of required files (phpfaces:require)
	 * @return string[] a string array of paths
	 */
	public function getRequiredFiles() {
		return $this->requireFiles;
	}
	
	/**
	 * Returns an array of managed beans (managed-bean)
	 * @return PhpFaces_ManagedBeanConfigEntry[] an array of managed config bean entries 
	 */
	public function getManagedBeans() {
		return $this->managedBeans;
	}
	
	/**
	 * Returns an array of navigation rules (navigation-rule
	 * @return PhpFaces_NavigationRuleConfigEntry[] an array of navigation rule entries 
	 */
	public function getNavigationRules() {
		return $this->navigationRules;
	}
	
	/**
	 * Returns an array of navigation handlers
	 * @return array an array of class names to decorate, in order
	 */
	public function getNavigationHandlers() {
		return $this->navigationHandlers;
	}
	
	/**
	 * Returns an array of view handlers
	 * @return array an array of class names to decorate, in order
	 */
	public function getViewHandlers() {
		return $this->viewHandlers;
	}
}

/*
$config = new FacesConfig( "../../faces-config.xml" );
$config->parse();

print_r( $config );
*/
?>